'use strict';

module.exports = () => {
   const model = {};
   /**
    * 第一步，插件库会自动解析 token 请求头里的 clientId 和 clienSecret ，进行第一步验证，
    * 只有当是可被授权的客户端并且密码正确再执行下一步。
    * 在这里，我的客户端 my_app 的授权密码 my_secret 是提前与服务端约定好，
    * 或者服务端给客户端的一个口令，服务端可以硬编码或者从数据库里进行查询。
    * @param clientId
    * @param clientSecret
    * @param callback
    * @returns {Promise<void>}
    */
   model.getClient = async (clientId, clientSecret, callback) => {
      if (clientId === 'my_app' && clientSecret === 'my_secret') {
         callback(null, {clientId, clientSecret});
         return;
      }
      callback(null, null);
   };
   /**
    * 第二步，插件库自动解析 token 请求头里的 grantType ，只有当该客户端满足请求的授权模式时，才执行下一步。
    * @param clientId
    * @param grantType
    * @param callback
    */
   model.grantTypeAllowed = (clientId, grantType, callback) => {
      let allowed = false;
      if (grantType === 'password' && clientId === 'my_app') {
         allowed = true;
      }
      callback(null, allowed);
   };
   /**
    * password 模式独有的步骤，插件库自动解析 token 请求 body 里的 username 和 password 字段传到改 API 里，
    * 这里使用了 egg-mongoose 插件从数据库里查询用户名，并通过 bcrypt 加密库验证密码的正确性。
    * 这里会把 user._id 作为用户验证正确的回调参数传给下一步进行保存，可以对应每个 token 和具体用户。
    * @param username
    * @param password
    * @param callback
    * @returns {Promise<void>}
    */
   model.getUser = async (username, password, callback) => {
      const user = await app.model.User.findOne({
         $or: [
            {email: username},
            {name: username},
         ]
      });
      if (!user) {
         callback(null, null);
         return;
      }
      const result = await bcrypt.compare(password, user.password);
      if (!result) {
         callback(null, null);
      } else {
         callback(null, {id: user._id});
      }
   };
   /**
    * 第四步也就是最后一步，非常简单，将返回给用户的 token 进行保存入库，供之后服务端查询 token 的有效性和对应的用户。
    * @param accessToken
    * @param clientId
    * @param expires
    * @param user
    * @param callback
    * @returns {Promise<void>}
    */
   model.saveAccessToken = async (accessToken, clientId, expires, user, callback) => {
      await app.model.OauthToken({accessToken, expires, clientId, user}).save();
      callback(null);
   };
   /**
    * 验证 token 正确性的中间件 app.oauth.authorise() 调用的唯一函数 getAccessToken 非常简单，直接从数据库中查询该 token 并进行回调，插件库会自动比对有效期。
    * @param bearerToken
    * @param callback
    * @returns {Promise<void>}
    */
   model.getAccessToken = async (bearerToken, callback) => {
      const token = await app.model.OauthToken.findOne({accessToken: bearerToken});
      callback(null, token);
   };
   return model;
};
